﻿
Imports System.Runtime.CompilerServices
Imports System.Net.Sockets
Imports osi.root.envs
Imports osi.root.formation
Imports osi.root.connector
Imports osi.root.utils
Imports osi.root.constants

Public Module _socket
    <Extension()> Public Function receive(ByVal s As Socket,
                                          ByVal buff() As Byte,
                                          ByVal offset As Int32,
                                          ByVal count As Int32,
                                          ByVal rate_sec As Int32,
                                          ByVal result As pointer(Of Int32)) As event_comb
        Return receive(s, buff, offset, count, rate_sec, result, True)
    End Function

    <Extension()> Public Function partial_receive(ByVal s As Socket,
                                                  ByVal buff() As Byte,
                                                  ByVal offset As Int32,
                                                  ByVal count As Int32,
                                                  ByVal rate_sec As Int32,
                                                  ByVal result As pointer(Of Int32)) As event_comb
        Return receive(s, buff, offset, count, rate_sec, result, False)
    End Function

    Private Function receive(ByVal s As Socket,
                             ByVal buff() As Byte,
                             ByVal offset As Int32,
                             ByVal count As Int32,
                             ByVal rate_sec As Int32,
                             ByVal result As pointer(Of Int32),
                             ByVal until_full As Boolean) As event_comb
        Dim end_ms As Int64 = 0
        Return New event_comb(Function() As Boolean
                                  If s Is Nothing OrElse
                                     offset < 0 OrElse
                                     count < 0 OrElse
                                     array_size(buff) < offset + count Then
                                      Return False
                                  ElseIf count = 0 Then
                                      Return goto_end()
                                  Else
                                      Return eva(end_ms,
                                                 rate_to_ms(rate_sec, count) + nowadays.milliseconds()) AndAlso
                                             eva(result, 0) AndAlso
                                             goto_next()
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return waitfor(Function() As Boolean
                                                     Return buffered_bytes(s) <> 0
                                                 End Function,
                                                 end_ms - nowadays.milliseconds()) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Dim b As Int32 = 0
                                  b = buffered_bytes(s)
                                  If b < 0 Then
                                      Return False
                                  ElseIf b = 0 Then
                                      If nowadays.milliseconds() < end_ms Then
                                          'odd
                                          Return goto_prev()
                                      Else
                                          Return (Not until_full) AndAlso
                                                 goto_end()
                                      End If
                                  Else
                                      Dim r As Int32 = 0
                                      r = min(count, b)
                                      Try
                                          assert(s.receive(buff, offset, r, SocketFlags.None) = r)
                                      Catch
                                          Return False
                                      End Try
                                      offset += r
                                      count -= r
                                      assert(eva(result, (+result) + r))
                                      Return If((until_full AndAlso count > 0), goto_prev(), goto_end())
                                  End If
                              End Function)
    End Function

    <Extension()> Public Function send(ByVal s As Socket,
                                       ByVal buff() As Byte,
                                       ByVal offset As Int32,
                                       ByVal count As Int32,
                                       ByVal rate_sec As Int32,
                                       Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Dim single_send_size As Int32 = 0
        Dim end_ms As Int64 = 0
        Return New event_comb(Function() As Boolean
                                  If s Is Nothing OrElse
                                     offset < 0 OrElse
                                     count < 0 OrElse
                                     array_size(buff) < offset + count Then
                                      Return False
                                  ElseIf count = 0 Then
                                      Return goto_end()
                                  Else
                                      Return eva(single_send_size, s.send_buff_size()) AndAlso
                                             single_send_size > 0 AndAlso
                                             eva(end_ms,
                                                 rate_to_ms(rate_sec, count) + nowadays.milliseconds()) AndAlso
                                             eva(result, 0) AndAlso
                                             goto_next()
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return waitfor(AddressOf s.poll_write,
                                                 end_ms - nowadays.milliseconds()) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If s.poll_write() Then
                                      Dim r As Int32 = 0
                                      r = min(count, single_send_size)
                                      assert(r > 0)
                                      Try
                                          assert(s.send(buff, offset, r, SocketFlags.None) = r)
                                      Catch
                                          Return False
                                      End Try
                                      offset += r
                                      count -= r
                                      assert(eva(result, (+result) + r))
                                      Return If(count > 0, goto_prev(), goto_end())
                                  ElseIf nowadays.milliseconds() >= end_ms Then
                                      Return False
                                  Else
                                      'odd
                                      Return goto_prev()
                                  End If
                              End Function)
#If 0 Then
        Return lifetime_event_comb(rate_to_ms(rate_sec, count),
                                   Function() As Boolean
                                       If s Is Nothing OrElse
                                          offset < 0 OrElse
                                          count < 0 OrElse
                                          array_size(buff) < offset + count Then
                                           Return False
                                       ElseIf count = 0 Then
                                           Return goto_end()
                                       Else
                                           s.Blocking() = False
                                           Return eva(result, 0) AndAlso
                                                  goto_next()
                                       End If
                                   End Function,
                                   Function() As Boolean
                                       Dim r As Int32 = 0
                                       r = s.send(buff, offset, count, SocketFlags.None)
                                       If r > 0 Then
                                           offset += r
                                           count -= r
                                           assert(eva(result, (+result) + r))
                                           Return count > 0 OrElse
                                                  (eva(s.Blocking(), True) AndAlso
                                                   goto_end())
                                       Else
                                           Return waitfor(constants.socket_poll_interval_ms)
                                       End If
                                   End Function)
#End If
    End Function
End Module
